package com.liuqi.iot.service;

import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.liuqi.common.base.bean.query.DynamicQuery;
import com.liuqi.common.exception.AppException;
import com.liuqi.iot.bean.dto.DeviceModelDTO;
import com.liuqi.iot.bean.dto.DeviceModelFieldDTO;
import com.liuqi.iot.bean.dto.DeviceModelPublishedDTO;
import com.liuqi.iot.bean.query.DeviceModelFieldQuery;
import com.liuqi.sys.common.ErrorCodes;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 模型服务
 *
 * @author LiuQi 2025/3/23-15:28
 * @version V1.0
 **/
@Service
public class DeviceModelService {
    @Autowired
    private DeviceModelEntityService modelEntityService;

    @Autowired
    private DeviceModelFieldEntityService modelFieldEntityService;

    @Autowired
    private DeviceModelPublishedEntityService publishedEntityService;

    @Autowired
    private HttpServletResponse response;

    /**
     * 根据id查找记录
     */
    public Optional<DeviceModelDTO> findById(String id) {
        DeviceModelDTO model = modelEntityService.findById(id)
                .orElse(null);
        if (null != model) {
            model.setFields(getModelFields(id));
        }

        return Optional.ofNullable(model);
    }

    /**
     * 根据模型查询字段列表
     */
    public List<DeviceModelFieldDTO> getModelFields(String id) {
        DeviceModelFieldQuery query = new DeviceModelFieldQuery();
        query.setModelId(id);
        return modelFieldEntityService.query(query);
    }

    /**
     * 发布模型
     * @param id 模型id
     */
    @Transactional
    public void publish(String id) {
        DeviceModelDTO model = this.findById(id).orElseThrow(AppException.supplier(ErrorCodes.IOT_MODEL_NOT_EXISTS));
        if (CollectionUtils.isEmpty(model.getFields())) {
            throw AppException.of(ErrorCodes.IOT_MODEL_FIELDS_EMPTY);
        }

        DeviceModelPublishedDTO published = new DeviceModelPublishedDTO();
        BeanUtils.copyProperties(model, published);
        publishedEntityService.deletePhysical(id);
        publishedEntityService.insert(published);
        model.setStatus(1);
        modelEntityService.update(model);
    }

    /**
     * 模型下线
     */
    @Transactional
    public void offline(String id) {
        DeviceModelDTO model = new DeviceModelDTO();
        model.setStatus(3);
        model.setId(id);
        modelEntityService.update(model);
        publishedEntityService.deletePhysical(id);
    }

    /**
     * 保存模型记录
     */
    @Transactional
    public DeviceModelDTO save(DeviceModelDTO req) {
        // 有两种情况，一是新增，二是更新
        if (StringUtils.isBlank(req.getId())) {
            // 没有id时是新增
            req = modelEntityService.insert(req);
            List<DeviceModelFieldDTO> fields = req.getFields();
            if (!CollectionUtils.isEmpty(fields)) {
                String modelId = req.getId();
                fields.forEach(field -> field.setModelId(modelId));
                fields = modelFieldEntityService.insert(fields);
                req.setFields(fields);
            }

            return req;
        }

        req.setStatus(2);

        // 更新
        modelEntityService.update(req);

        // 处理字段更新
        // 先查出库中已存在的字段
        String modelId = req.getId();
        List<DeviceModelFieldDTO> dbFields = this.getModelFields(modelId);
        List<DeviceModelFieldDTO> fields = req.getFields();
        Map<String, DeviceModelFieldDTO> dbFieldMap = dbFields.stream()
                .collect(Collectors.toMap(DeviceModelFieldDTO::getId, i -> i));

        // 判断哪些字段要删除，哪些要新增或更新
        fields.forEach(field -> {
            field.setModelId(modelId);
            DeviceModelFieldDTO dbField = dbFieldMap.get(field.getId());
            dbFieldMap.remove(field.getId());
            if (null == dbField) {
                // 新增
                DeviceModelFieldDTO savedField = modelFieldEntityService.insert(field);
                field.setId(savedField.getId());
            } else {
                // 更新
                modelFieldEntityService.update(field);
            }
        });

        // 剩下的字段需要删除
        Set<String> restIds = dbFieldMap.keySet();
        if (!CollectionUtils.isEmpty(restIds)) {
            modelFieldEntityService.delete(restIds);
        }

        return req;
    }

    /**
     * 下载导入模板
     */
    public void downloadTemplate() {
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=modelTemplate.xlsx");
            ServletOutputStream out = response.getOutputStream();
            ExcelWriter writer = ExcelUtil.getWriter(true)
                    .writeHeadRow(Arrays.asList("模型名称", "属性名称", "属性编码", "属性单位"));

            writer.flush(out, true);
            writer.close();
            IoUtil.close(out);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 数据导入
     * @param file 导入文件
     */
    public void importData(String categoryId, MultipartFile file) {
        // 查询分类下的所有模型
        List<DeviceModelDTO> models = modelEntityService.dynamicQuery(DynamicQuery.create().eq("category_id", categoryId));
        Map<String, DeviceModelDTO> modelMap = models.stream()
                .collect(Collectors.toMap(DeviceModelDTO::getName, d -> d));

        try {
            ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
            List<List<Object>> list = reader.read();
            Map<String, List<DeviceModelFieldDTO>> modelFieldMap = new HashMap<>(16);
            for (int i = 1; i < list.size(); i++) {
                List<Object> row = list.get(i);
                if (row.size() != 5) {
                    continue;
                }

                String modelName = String.valueOf(row.get(0));
                if (StringUtils.isBlank(modelName)) {
                    throw AppException.of(ErrorCodes.IOT_MODEL_IMPORT_NAME_EMPTY);
                }

                String name = String.valueOf(row.get(1));
                String code = String.valueOf(row.get(2));
                if (StringUtils.isBlank(name) || StringUtils.isBlank(code)) {
                    throw AppException.of(ErrorCodes.IOT_MODEL_IMPORT_CODE_OR_NAME_EMPTY);
                }

                DeviceModelFieldDTO field = new DeviceModelFieldDTO();
                field.setCode(code);
                field.setName(name);
                field.setType(0);
                modelFieldMap.computeIfAbsent(modelName, n -> new ArrayList<>(16))
                        .add(field);
            }

            modelFieldMap.forEach((modelName, fields) -> {
                DeviceModelDTO model = modelMap.get(modelName);
                if (null == model) {
                    model = new DeviceModelDTO();
                    model.setName(modelName);
                    model.setCategoryId(categoryId);
                    model = modelEntityService.insert(model);
                } else {
                    // 先清理属性
                    modelFieldEntityService.deleteByModel(model.getId());
                }

                String modelId = model.getId();
                fields.forEach(field -> field.setModelId(modelId));
                modelFieldEntityService.insert(fields);
            });

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
